package im.nio;

/**
 * Created by gzhd on 2017/12/1.
 */


import javax.swing.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO客户端
 *
 * @author yangtao__anxpp.com
 * @version 1.0
 */
public class ClientHandle implements Runnable {
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean started;
    private static final int TIMEOUT = 3000;
    String userName = null;
    String tip = "";
    private static final int BUF_SIZE = 1024;

    public ClientHandle(String ip, int port) {
        this.host = ip;
        this.port = port;
        try {
            //创建选择器
            selector = Selector.open();
            //打开监听通道
            socketChannel = SocketChannel.open();
            //如果为 true，则此通道将被置于阻塞模式；如果为 false，则此通道将被置于非阻塞模式
            socketChannel.configureBlocking(false);//开启非阻塞模式
            System.out.println("客户端启动完成");
            started = true;
            userName = JOptionPane.showInputDialog(tip + "输入用户名");    //①
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop() {
        started = false;
    }

    @Override
    public void run() {
        int ti;
        try {
            doConnect();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        //循环遍历selector
        while (started) {
            try {
//                //无论是否有读写事件发生，selector每隔1s被唤醒一次
                ti = selector.select(TIMEOUT);
                if (ti == 0) {
//                    System.out.println("没有收到任何响应··再等");
                    continue;
                }
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        //selector关闭后会自动释放里面管理的资源
        if (selector != null)
            try {
                selector.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }

    private void handleInput(SelectionKey key) throws Exception {
        if (key.isValid()) {
            if (key.isConnectable()) {
                handleConnectable(key);
            }
            //读消息
            if (key.isReadable()) {
                handleReadable(key);
            } else if (key.isWritable()) {
                handleWritable(key);
            }


        }
    }

    private void handleWritable(SelectionKey key) {

    }

    private void handleReadable(SelectionKey key) throws Exception {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        handleMsg(channel, msg);

//        SocketChannel sc = (SocketChannel) key.channel();
//        //创建ByteBuffer，并开辟一个1M的缓冲区
//        ByteBuffer buffer = ByteBuffer.allocate(1024);
//        //读取请求码流，返回读取到的字节数
//        int readBytes = sc.read(buffer);
//        //读取到字节，对字节进行编解码
//        if (readBytes > 0) {
//            //将缓冲区当前的limit设置为position=0，用于后续对缓冲区的读取操作
//            buffer.flip();
//            //根据缓冲区可读字节数创建字节数组
//            byte[] bytes = new byte[buffer.remaining()];
//            //将缓冲区可读字节数组复制到新建的数组中
//            buffer.get(bytes);
//            String result = new String(bytes, "UTF-8");
//            System.out.println("客户端收到消息：" + result);
//        }
//        //没有读取到字节 忽略
////              else if(readBytes==0);
//        //链路已经关闭，释放资源
//        else if (readBytes < 0) {
//            key.cancel();
//            sc.close();
//        }

    }

    private void handleMsg(SocketChannel channel, String msg) {
        if (msg.equals(IMMsgProtocol.NAME_REP)) {
            tip = "用户名重复！请重新";
            System.out.println("tip");
            return;
        }
        // 如果服务器返回登录成功，结束循环
        if (msg.equals(IMMsgProtocol.LOGIN_SUCCESS)) {
            System.out.println(userName+"登录成功");
            return;
        }
        if (msg.contains(IMMsgProtocol.SEND_MYSELF)) {
            String tmpUser=msg.split(IMMsgProtocol.SEND_MYSELF)[0];
            if (tmpUser.equals(userName)) {
                return;
            }
            else {
                System.out.println(msg);
                return;
            }
        }
        System.out.println(msg);


    }

    private void handleConnectable(SelectionKey key) throws Exception {
        System.out.println("处理Connectable事件····");
        SocketChannel channel = (SocketChannel) key.channel();
        if (channel.isConnectionPending()) {
            channel.finishConnect();
        }
        channel.configureBlocking(false);
//        发送自己的用户名用于登录
        String msg = IMMsgProtocol.USER_ROUND + userName + IMMsgProtocol.USER_ROUND;
        channel.write(ByteBuffer.wrap(msg.getBytes()));
        channel.register(this.selector, SelectionKey.OP_READ);
    }

    //异步发送消息
    private void doWrite(SocketChannel channel, String request) throws IOException {
        String outStr = "";
//        p2p
        if (request.indexOf(":") > 0 && request.startsWith("//")) {
            outStr = handleP2PMsg(request);
        } else {
//            p2all
            outStr = handleP2AllMsg(request);
        }
        //将消息编码为字节数组
        byte[] bytes = outStr.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
        //****此处不含处理“写半包”的代码
    }

    private String handleP2AllMsg(String request) {
        return IMMsgProtocol.MSG_ROUND + request + IMMsgProtocol.MSG_ROUND;
    }

    private String handleP2PMsg(String request) {
        String resultStr = "";
//        取出信息
        request = request.substring(2);
//        拼接
        resultStr = IMMsgProtocol.PRIVATE_ROUND +
                request.split(":")[0] + IMMsgProtocol.SPLIT_SIGN + request.split(":")[1] +
                IMMsgProtocol.PRIVATE_ROUND;
        return resultStr;
    }


    private void doWriteBak(SocketChannel channel, String request) throws IOException {

        //将消息编码为字节数组
        byte[] bytes = request.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
        //****此处不含处理“写半包”的代码
    }

    private void doConnect() throws IOException {
        if (socketChannel.connect(new InetSocketAddress(host, port))) ;
        else socketChannel.register(selector, SelectionKey.OP_CONNECT);
    }

    public void sendMsg(String msg) throws Exception {
        socketChannel.register(selector, SelectionKey.OP_READ);
        doWrite(socketChannel, msg);
    }
}
